#ifndef _POINT_
#define _POINT_


#include <iostream>
#include <type_traits>
#include <vector>
#include <algorithm>
#include <cassert>
#include "math.h"
using namespace std;
/**
 *@brief Point<DIM>点类
 *
 */
template<unsigned int DIM>
class Point
{
protected:
    vector<double> value{vector<double>(DIM,0.0)};/*<存储各个维度的坐标 */
public:
    /**
     * @brief 无参构造函数
     *
     */
    Point() = default;
    /**
	 * @brief 默认析构函数
	 *
	 */
	~Point() = default;
        //拷贝构造函数
	Point(const Point<DIM>& _point);
	//移动构造函数
	Point(Point<DIM>&& _point);
    /**
	 * @brief 左值引用含参构造函数。
	 *
	 * @param Point_vector:左值引用，包含坐标。
	 */
	Point(const vector<double>& _point_vector);
	/**
	 * @brief 右值引用含参构造函数。
	 *
	 * @param Point_vector:右值引用，包含坐标。
	 */
	Point(vector<double>&& _point_vector);
    /**
	 * @brief 初始化列表含参构造函数。
	 *
	 * @param Point_list:初始化列表。
	 */
	Point(const std::initializer_list<double>& _point_list);
	Point<DIM>& operator=(const Point<DIM>& rhs)
	 {
			if(this == &rhs)
			{
				return *this;
			}
			this->value = rhs.getPoint();
			return *this;
	   }
	/**
	 * @brief: 重载[]操作符
	 *
	 * @param d 第d个维度
	 *
	 * @return 第d个维度的坐标
	 */
	const double& operator[](unsigned int d) const;
	const vector<double>& getPoint() const;
    /**
	 * @brief 友元函数，输出Point
	 *
	 * @param os:ostream
	 * @param p:Point<DIM>
	 *
	 * @return
	 */
	template<unsigned int D>
	friend ostream& operator<<(ostream& os, const Point<D> & p);
    /**
	 * @brief 友元函数，计算两点间距离。
	 *
	 * @param p1:Point<DIM>
	 * @param p2:Point<DIM>
	 *
	 * @return
	 */
	template<unsigned int D>
	friend double distance(const Point<DIM> p1, const Point<D> p2);
	template<unsigned int D>
	friend Point<DIM> operator+(const Point<D> p1, const Point<D> p2);
	template<unsigned int D>
	friend Point<DIM> operator-(const Point<D> p1, const Point<D> p2);
	template<unsigned int D>
	friend Point<DIM> operator*(double a, const Point<D> p);
	template<unsigned int D>
	friend Point<DIM> operator/(const Point<D> p, double a);
};

template<unsigned int DIM>
Point<DIM>::Point(const vector<double>& p_vector)
{
    if(p_vector.size() != DIM)
    {
		cerr << "Point : Dimension not match." << endl;
		exit(EXIT_FAILURE);
    }
    copy(cbegin(p_vector), cend(p_vector), begin(value));
};

template<unsigned int DIM>
Point<DIM>::Point(vector<double>&& p_vector)
{
    if(p_vector.size() != DIM)
    {
		cerr << "Point : Dimension not match." << endl;
		exit(EXIT_FAILURE);
    }
    swap(p_vector,value);
	p_vector.clear();

};

template <unsigned int DIM>
Point<DIM>::Point(const Point<DIM> &_point)
{
	value = _point.value;
};

template <unsigned int DIM>
Point<DIM>::Point(Point<DIM>&& _point)
{
	swap(value,_point.value);
};


template<unsigned int DIM>
Point<DIM>::Point(const std::initializer_list<double>& Point_list)
{
    if(Point_list.size() != DIM)
    {
		cerr << "Point : Dimension not match." << endl;
		exit(EXIT_FAILURE);
    }
    copy(cbegin(Point_list), cend(Point_list),begin(value));
};

template<unsigned int DIM>
const double& Point<DIM>::operator[](unsigned int d) const
{
	assert(d >=0 && d < DIM);
    return value.at(d);
}

template <unsigned int DIM>
const vector<double>& Point<DIM>::getPoint() const
{
	return value;
}

template<unsigned int D>
ostream& operator<<(ostream& os, const Point<D> & p)
{
    os << "(";
    for(int d = 0; d < D-1; ++d)
    {
        os << p[d] << ", ";
    }
    os << p[D-1] <<")\n";
    return os;
}

template<unsigned int D>
double distance(const Point<D> p1, const Point<D> p2)
{
    double temp;
    for(unsigned int d = 0; d < D; d++)
        temp += pow((p1.value[d] - p2.value[d]),2);
    return sqrt(temp);
}


template <unsigned int D>
Point<D> operator+(const Point<D> p1, const Point<D> p2)
{
	vector<double> res;
	for(int i = 0; i < D; i++)
	{
		res.push_back(p1[i]+p2[i]);
	}
	return Point<D>{res};
};
template<unsigned int D>
Point<D> operator-(const Point<D> p1, const Point<D> p2)
{
	vector<double> res;
	for(int i = 0; i < D; i++)
	{
		res.push_back(p1[i]-p2[i]);
	}
	return Point<D>{res};
};
template<unsigned int D>
Point<D> operator*(double a, const Point<D> p)
{
	vector<double> res;
	for(int i = 0; i < D; i++)
	{
		res.push_back(a * p[i]);
	}
	return Point<D>{res};
};
template<unsigned int D>
Point<D> operator/(const Point<D> p, double a)
{
	assert( a<0.0 || a>0.0 );
	vector<double> res;
	for(int i = 0; i < D; i++)
	{
		res.push_back(p[i]/a);
	}
	return Point<D>{res};
}
#else
// DO NOTHING.
#endif
